package pzl.kt.study.base

import android.graphics.Paint
import android.widget.TextView
import org.junit.Test
import java.nio.file.Files
import java.nio.file.Paths

class Functions {

    //可变参数，修饰符，vararg
    @Test
    fun test1() {
        可变参数的函数(1, 3, 100, "是么", 1.22, '$')
    }

    fun 可变参数的函数(vararg x: Any) {
        var count = 0
        for (item in x) {
            count++
        }
        println("传入的参数有${count}个")
    }


    //在一些函数是接收可变参数的时候，如果传入的是数组对象，在传入前进行展开操作
    @Test
    fun 展开操作符() {
        var arr = arrayOf(1, 4, 32, 4, 53)
        可变参数的函数(arr)//1
        可变参数的函数(*arr)//5
    }

    //测试本地函数 嵌套函数
    @Test
    fun test2() {
        printArea(314, 1314)

        printArea2(3.14f, 4.32f)

        localFun(1, 16)
        println()
        localFun2(1, 16)
    }


    //把小函数声明在其它函数内部，被称之为“本地函数”或“嵌套函数”，甚至可以多层嵌套
    //本地函数只服务于父级函数，对外部无效。这对大函数隐藏内部细节有帮助
    fun printArea(width: Int, height: Int) {
        fun clcArea(w: Int, h: Int): Int {
            return w * h
        }

        var area = clcArea(width, height)
        println("area = " + area)
    }

    fun printArea2(width: Float, height: Float) {
        fun clcArea() = width * height
        var area = clcArea()
        println("area = " + area)
    }

    fun localFun(start: Int, end: Int) {
        fun isThree(x: Int): Boolean = x % 3 == 0
        fun isFive(x: Int): Boolean = x % 5 == 0
        for (x in start..end) {
            if (isThree(x) && isFive(x)) {
                println("${x} 被3和5整除")
            } else if (isFive(x)) {
                println("${x} 被5整除")
            } else if (isThree(x)) {
                println("${x} 被3整除")
            } else {
                println("${x} 其它")
            }
        }
    }


    fun localFun2(start: Int, end: Int) {
        fun isThree(x: Int): Boolean = x % 3 == 0
        fun isFive(x: Int): Boolean = x % 5 == 0
        for (x in start..end) {
            when {
                isThree(x) && isFive(x) -> println("${x} 被3和5整除")
                isFive(x) -> println("${x} 被5整除")
                isThree(x) -> println("${x} 被3整除")
                else -> println("${x} 其它")
            }
        }
    }


    //顶层函数
    //不同于任何源码文件的 集团（ class 、对象、 interface ）而是直接定义在源码文件中的它们所有小集团的层级之上。
    //一些顶层函数用来可以简化一些操作，让你的代码变得更加简洁易懂
    //with,run,apply,let,also,use
    @Test
    fun topFun() {
        println(getTime())
    }

    //默认参数
    @Test
    fun testDefaultParams() {
        println(defauParams())
        println(defauParams(199, "大街网", false))
    }

    fun defauParams(a: Int = 3, str: String = "defaultStr", b: Boolean = true): String {
        return a.toString() + "-" + str + "-" + b;
    }


    //函数操作符重载
    //内置操作符     函数名
    //!x              x.not()
    //-y              y.unaryMinus()
    //+z              z.unaryPlus()
    //a..b            a.rangeTo(b)
    //c+d             c.plus(d)
    //e-f             c.minus(f)
    //g*h             g.times(h)
    //i/j             i.div(j)
    //m%n             m.mod(n）
    @Test
    fun testOperator() {
        var m1 = Matrix(1, 2, 3, 4)
        var m2 = Matrix(4, 3, 2, 1)
        var m3 = m1 + m2
        println(m3.toString())
    }

    //操作符重载函数
    //数组取元素的中括号[]语法对应 get set 中括号中的数字会传递给 get set 函数
    @Test
    fun testOperator2() {
        var m = Matrix2(1, 2, 3, 4)
        println(m[1, 1])
        m[1, 1] = 1000
        println(m[1, 1])
    }

    //操作符小括号（） 等于 重载操作符函数Invoke

    //用 、＞＝、＜、＜＝这 个操作符进
    @Test
    fun testOperator3() {
        var 顾客1 = Member("vip", 4)
        var 顾客2 = Member("svip", 1)
        if (顾客1 > 顾客2) println("顾客1>顾客2 " + (顾客1 > 顾客2))
        if (顾客1 < 顾客2) println("顾客1<顾客2 " + (顾客1 < 顾客2))
    }


    //有时候很想对现有的类进行改进，加入新函数，但是苦于无权限更改或者没有源代码
    //你可能会想到用面向对象（具体请见本书的面向对象篇）的思维方法，继承这个类，并在
    //类实现上增加函数，但这个方法并不总是有效。因为有些类可能己经是 Final 不可继承的，
    //而且你可能也没法对既有类型的实例进行控制。
    @Test
    fun 函数扩展() {
        //1.
        fun Int.平方() = this * this
        println("3的平方 = " + 3.平方())

        //2.限制范围：使用顶层函数扩展那么它就是全局的，使用类扩展那么它就只能在类中使用
        //3.若父类、接口中有同名函数，则函数的扩展会失效
        //4.扩展函数可在子类中“重载”
    }

    //中缀函数，对2元单元进行操作
    @Test
    fun tesInfix() {
        var c1 = City("长沙", 100)
        var c2 = City("上海", 500)
        var a = c1 到 c2
        println(a)
    }

    //字面量—字面量—字面量—字面量—字面量 呃呃
    //函数字面量在高阶函数里面称呼为“闭包”
    @Test
    fun testZiMianLiang() {
        //不用写变量 直接用文字写出，如：字符串字面量“hello world”，Double字面量12.22，Int字面量4 ...
        //字面量可赋值给一个变量或常量

        //函数字面量
        { println("hello world") }
        //函数字面量赋值给一个变量
        var printHW = { println("hello world") }

        //函数字面量也可接受参数 , "->"表示后面的是执行语句
        var printMSG = { msg: String -> println(msg) }
        printMSG("懵逼了吧 kotlin")

        //参数类型推断，可省略参数类型
        //var printSth = { msg -> println(msg) }
        //printSth("为什么 为什么")

        //当只有一个参数的时候，可简化为这样
        //var printFun = { println(it) }

    }

    //标准库函数 --------------------------
    @Test
    fun testStdLibApply() {
        //apply函数,返回当前自己对象
        var runnable = Runnable { println("Running ... 【apply函数】") }
        //在闭包（函数字面量在高阶函数中的称呼）中配置成守护线程
        Thread(runnable).apply { setDaemon(true) }.start()

        //传统方式
        var task = Runnable { println("Running ... 【传统】") }
        var thread = Thread(task)
        thread.setDaemon(true)
        thread.start()
    }

    @Test
    fun testStdLibLet() {
        //let函数
        //默认当前对象作为闭包的it参数，返回值是函数的最后一行，或指定return
        "你好 靠，特灵~".let {
            println(it)
        }

        //let函数
        var list = ArrayList<String>()
        var what = list.let {
            it.add("(ーー゛)")
            it.add("建国伟业")
            it.get(1)
        }
        println(what)

    }

    @Test
    fun testStdLibWith() {
        //with函数
        //一个对象要调用多个方法时
        var apple = Apple()
        with(apple) {
            color = "米红色"
            setWeight(1.23)
            setPrice(16.8f)
            address = "朝阳区"
        }

        //with函数+let
        with(ArrayList<String>()) {
            add("有一说一")
            add("说一不二")
            add("说三道四")
            add("四六不分")
            add("五迷三道")
            println(this)
        }.let {
            println(it)
        }
    }

    @Test
    fun testStdLibRun() {
        //run函数
        //run 函数和 apply 函数很像，只不过run函数是使用最后一行的返回，apply返回当前自己的对象，run就是with和let的组合式扩展：
        var s = ArrayList<String>().run {
            add("Test1")
            add("Test2")
            add("Test3")
            get(2)
        }
        println(s)
    }

    @Test
    fun testStdLibLazy() {
        //lazy函数
        var lazyString = lazy { 耗时模拟() }
        var string = lazyString.value
        println(string)
    }


    fun 耗时模拟(): String {
        println("模拟开始-")
        Thread.sleep(5000)
        return "网络访问数据的... .. ."
    }

    @Test
    fun testStdLibUse() {
        //use函数
        //use和try语句有点相似。use被用于一个可closable的实例且有一个可操作这个closable的闭包。use会安全地调用这个函数，在函数调用完成后关闭占用的资源，不管是否出现异常
        var input =
            Files.newInputStream(Paths.get("C:\\Users\\Administrator\\Desktop\\JL\\AndGO\\kt-practice\\app\\src\\main\\AndroidManifest.xml"))
        var byte = input.use({ input.read() })
        println(byte)
    }

    @Test
    fun testStdLibRepeat() {
        //Repeat函数
        //重复执行一个闭包指定的次数
        repeat(5, {
            var c = ArrayList<String>().run {
                add("无人之岛")
                add("天外之物")
                add("打开思维")
                toString()
            }
            println(c)
        })

    }

    @Test
    fun testStdLibRequireAssertCheck() {
        //require/assert/check 添加程序说明的三件套函数
    }

    @Test
    fun testStdLibRequired() {
        //require会抛出一一个异常，用来确保参数符合输入条件。
        doTest1(4)
        doTest1(-1)
    }

    fun doTest1(x: Int) {
        require(x > 0, { "x必须大于0" })
        println(x)
    }


    @Test
    fun test泛型() {
        var r1 = outString(1, 4, 5)
        println(r1)
    }

    fun <T> outString(a: T, b: T, c: T): String {
        return "$a,$b,$c"
    }


    @Test
    fun test泛型2() {
        var arr = arrayOf(1, 2, 2, 43, 2, 4, 42, 562, 564)
        println(arr.最大值())
        var arr2 = arrayOf(1.222,1222.2,4.444)
        println(arr2.最大值())
    }


    //泛型+函数扩展
    //fun Array.最大值():T 为扩展函数
    //fun <T> 申明使用的泛型类型
    // where T:Number,T:Comparable<T> ,对泛型T的限制：T属于Number类型、T实现Comparable接口
    // Array为函数扩展的接收者类型，this为函数扩展的接收者对象
    fun <T> Array<T>.最大值(): T where T : Number, T : Comparable<T> {
        var 预定最大值 = this[0] //假设第一个值作为最大值
        for (i in 1..lastIndex) {
            val ele = this[i]
            if (ele > 预定最大值) {
                预定最大值 = ele
            }
        }
        return 预定最大值
    }

}